# -*- coding: utf-8 -*-
# Create your views here.

import urllib
from django.db import connection
from django.db.models import Count, Max
from django.contrib import auth
from django.contrib.auth.decorators import login_required
from django.http import HttpResponse, HttpResponseRedirect, Http404
from django.shortcuts import render_to_response, get_object_or_404
from django.core.urlresolvers import reverse
from django.contrib.auth.models import User, AnonymousUser
from forms import BookmarkForm, TagForm, FeedUrlForm, DeliciousForm, TwitterForm, SubscriptionForm, FilterForm, OpmlForm

from stream import feeds, models, dbrequests, utils

try:
    import json
except:
    import simplejson as json

from conversion import Converter, alias

def response403():
    result = HttpResponse()
    result.status_code = 403
    return result

def getMessageSet(request, owner_id):
    try:
        owner_id = int(owner_id)
    except Exception:
        # TODO: should log the exception somehow.
        raise Http404()
    messages = models.Message.objects.filter(user__id = owner_id, ignored = False)
    if not (request.user.is_authenticated() and request.user.id == owner_id):
        messages = messages.filter(published = True)

    return messages

def ownerIdFromRequest(request):
    try:
        owner_id = request.GET.get('owner_id', None) # Whose entries to retrieve
        if owner_id is None:
            owner_id = request.user.id # Yes, this will fail if user is not authenticated
        owner_id = int(owner_id)
    except:
        raise Http404
    return owner_id

def jsonResponse(data):
    response = HttpResponse()
    json.dump(data, response)
    return response

def namesToPath(tag_list):
    result = tag_separator.join(map(urllib.quote, tag_list))
    return result

def pathToNames(path):
    result = map(urllib.unquote, path.split(tag_separator))
    return filter(lambda x: len(x) > 0, result)

def pathToTags(path):
    if path:
        names = pathToNames(path)
        return map(lambda x: models.Tag.objects.get_or_create(name = x)[0], names)
    else:
        return []

message_context = {
    'Message' : ['id', 'entry', 'tags', 'published'],
    'FeedEntry' : ['title', 'link', 'description', 'updated', 'created'],
    'BookmarkEntry' : ['link', 'created'],
    'DeliciousEntry' : ['link', 'title', 'created'],
    'TwitterEntry' : ['link', 'description', 'created'],
    'Tag' : ['name']
}

def entries(request):
    owner_id = ownerIdFromRequest(request)

    tag_path = request.GET.get('tags', '')
    tag_list = pathToNames(tag_path)
    messages = getMessageSet(request, owner_id)
    for tag_name in tag_list:
        messages = messages.filter(tags__name = tag_name)
    subscription_id = request.GET.get('subscription', None)
    if subscription_id is not None:
        subscription_id = int(subscription_id)
        messages = messages.filter(subscriptions__id = subscription_id)
    unread_only = request.GET.get('unread_only', None)
    if unread_only and utils.boolFromUrl(unread_only):
        messages = messages.filter(read = False)
    favorite_blogs_only = request.GET.get('favorite_only', None)
    if favorite_blogs_only and utils.boolFromUrl(favorite_blogs_only):
        messages = messages.filter(subscriptions__favorite = True)
    messages = messages.order_by('-entry__created')

    first = request.GET.get('first', None)
    count = request.GET.get('count', None)

    if first:
        messages = messages[first:]
    if count:
        messages = messages[:count]

    response_data = Converter(message_context).convert(messages)
    return jsonResponse(response_data)

def tags(request):
    """
    Tags of user with owner_id. Only tags for public messages are displayed if owner_id is not the current user
    """
    owner_id = ownerIdFromRequest(request)
    messages = getMessageSet(request, owner_id)

    context = {
        'Tag' : ['name', 'count']
    }

    result = dbrequests.getUserTags(owner_id, request.user.id != owner_id)
    result = map(lambda x: {'name' : x[0], 'count' : x[1]}, result)

    return jsonResponse(result)

@login_required
def subscriptions(request):
    """
    User's subscriptions
    """
    subscriptions = dbrequests.getSubscriptionsWithUnread(request.user.id)
    subscriptions = map(lambda x: { 'id' : x[0], 'name' : x[1], 'unread' : x[2]}, subscriptions)
    return jsonResponse(subscriptions)

def getSubscriptionById(subscription_id, expected_user_id):
    subscription_id = int(subscription_id)
    subscription = get_object_or_404(models.Subscription, id = subscription_id)
    if expected_user_id != subscription.user.id:
        raise Http404
    return subscription

@login_required
def subscriptionEdit(request, subscription_id = None):
    from xml.sax.saxutils import unescape as unescape_html
    """
    Extended subscription data: default tags and filters.
    """
    if request.method == 'GET':
        subscription = getSubscriptionById(subscription_id, request.user.id)
        subscription_form = SubscriptionForm(instance = subscription)
        filter_forms = map(lambda x: FilterForm(instance = x), subscription.filter_set.all())
        new_filter_form_text = FilterForm().as_table().replace('\n', ' ')
        return render_to_response('stream/subscriptionDetails.html', { 'viewer' : request.user, 'subscription_form' : subscription_form, 'filter_forms' : filter_forms, 'new_filter_form_text' : new_filter_form_text })
    elif request.method == 'POST':
        subscription_id = request.POST['id']
        subscription = get_object_or_404(models.Subscription, id = subscription_id)
        if request.user.id != subscription.user.id:
            raise Http404
        form = SubscriptionForm(request.POST, instance = subscription)
        if not form.is_valid():
            return render_to_response('stream/subscriptionDetails.html', { 'viewer' : request.user, 'form' : form })
        subscription = form.save()
        if form.cleaned_data['update']:
            messages = models.Message.objects.filter(subscriptions = subscription)
            for message in messages:
                message.published = subscription.published
                for tag in subscription.tags.all():
                    message.tags.add(tag)
                message.save()
        return redirectToTags([], request.user.id)

def getFilterById(filter_id, expected_user_id):
    filter_id = int(filter_id)
    result_filter = get_object_or_404(models.Filter, id = filter_id)
    if expected_user_id != result_filter.subscription.user.id:
        raise Http404
    return result_filter

@login_required
def filterEdit(request, filter_id):
    if filter_id is not None:
        edited_filter = getFilterById(filter_id, request.user.id)
        filter_form = FilterForm(request.POST, instance = edited_filter)
    else:
        filter_form = FilterForm(request.POST)
    if not filter_form.is_valid():
        # Ну я не знаю, что делать =(
        print filter_form.errors
        return
    filter = filter_form.save()
    if 'update' in request.POST and request.POST['update']:
        subscription = filter.subscription
        if subscription.user != request.user:
            raise Http404
        subscription.applyFilter(filter)
    return HttpResponse()

@login_required
def filterDelete(request, filter_id):
    deleted_filter = getFilterById(filter_id, request.user.id)
    deleted_filter.delete()
    return HttpResponse()

@login_required
def deleteSubscription(request):
    id = int(request.POST['id'])
    instance = get_object_or_404(models.Subscription, id = id)
    owner = instance.user
    if owner != request.user:
        raise Http404
    if 'type' not in request.POST:
        raise ValueError
    deletion_type = request.POST['type']
    if deletion_type == 'subscription':
        pass
    elif deletion_type == 'all_messages':
        instance.message_set.all().update(ignored = True)
    elif deletion_type == 'this_messages':
#        query = 'SELECT msg_id, COUNT(sub_id) AS "num_subscriptions" FROM (SELECT msg_id,\
#         smth_message_subscriptions.subscription_id as sub_id FROM\
#        (SELECT message_id as msg_id FROM smth_message_subscriptions WHERE subscription_id = 1) LEFT OUTER JOIN\
#        "smth_message_subscriptions" ON (msg_id = "smth_message_subscriptions"."message_id"))\
#        GROUP BY msg_id HAVING num_subscriptions = 1';
        for message in instance.message_set.all():
            if message.subscriptions.count() == 1:
                message.ignored = True
                message.save()
    else:
        raise ValueError
    instance.delete()
    return HttpResponse()

def default(request):
    return render_to_response('stream/root.html', { 'viewer' : request.user })

def stream(request, owner_id, tag_path = ''):
    """
    Display all items of owner_id if owner_id is the current user, or all published items otherwise.
    """
    try:
        owner_id = int(owner_id)
        User.objects.get(id = owner_id) # Check that such user exists.
    except:
        raise Http404
    viewer = request.user

    if owner_id == viewer.id:
        owner_cookie_value = 'true'
    else:
        owner_cookie_value = 'false'
    response = render_to_response('stream/index.html', {'viewer' : viewer})
    response.set_cookie('owner', owner_cookie_value)
    return response

tag_separator = ' '

def processTags(request, owner_id):
    if request.method == "POST":
        form = TagForm(request.POST)
        if form.is_valid():
            tag_list = form.cleaned_data['tags']
        else:
            tag_list = []
        return redirectToTags(tag_list, owner_id)

def redirectToTags(tag_list, owner_id):
    """
    Performs redirect to the necessary handler of tags.
    Most altering requests will call this function after processing to redirect to 'main page'
    """
    if len(tag_list) == 0:
        return HttpResponseRedirect(reverse('stream.views.stream', kwargs = { 'owner_id' : owner_id }))
    else:
        return HttpResponseRedirect(reverse('stream.views.stream', kwargs = { 'tag_path' : namesToPath(tag_list), 'owner_id' : owner_id }))

def generateAddForms():
    result = {}
    for cls in [BookmarkForm, FeedUrlForm, DeliciousForm, TwitterForm, OpmlForm]:
        result[cls._meta.FORM_NAME] = cls()
    return result

@login_required
def add(request):
    form_dictionary = generateAddForms()
    if request.method == "GET":
        FORM_TYPE_PARAM = 'type'
        if FORM_TYPE_PARAM in request.GET:
            form_type = request.GET[FORM_TYPE_PARAM]
            if form_type in form_dictionary:
                return render_to_response('stream/add.html', { 'viewer' : request.user, 'form' : form_dictionary[form_type]})
            else:
                return Http404()
        else:
            return render_to_response('stream/add.html', { 'viewer' : request.user })
    elif request.method == "POST":
        form_name = request.POST['form_name']
        form_prototype = form_dictionary[form_name]
        form = form_prototype.__class__(request.POST, request.FILES)
        if form.is_valid():
            try:
                form.save(request.user)
                return jsonResponse({ 'status' : 'success' })
            except models.ExternalChannelBase.ResourceError, e:
                pass
            except Exception, e:
                return jsonResponse({ 'status' : 'error' })
        form_dictionary[form_name] = form
        return render_to_response('stream/addForm.html', { 'form' : form })

def addForm(request):
    if request.method == "GET":
        form_dictionary = generateAddForms()
        return render_to_response('stream/addForm.html', { 'form' : form_dictionary[request.GET['form_name']]})

@login_required
def editMessage(request):
    try:
        id = int(request.POST['id'])
        tag_string = request.POST.get('tags', None)
        published = request.POST.get('published', None)
        read = request.POST.get('read', None)

        message = models.Message.objects.get(id = id)
        if (request.user.id != message.user.id):
            response = render_to_response('403.html')
            response.status_code = 403
            return response
        else:
            if tag_string is not None:
                tags = pathToTags(tag_string)
                message.tags.clear()
                for tag in tags:
                    message.tags.add(tag)
            if published is not None:
                message.published = (published == "true")
            if read is not None:
                message.read = utils.boolFromUrl(read)
            message.save()
            #Now return the updated entry as response
            return jsonResponse(Converter(message_context).convert(message))
    except models.Message.DoesNotExist:
        raise Http404, 'Message does not exist'

@login_required
def subscribeToUser(request):
    from stream.testcases import utils
    try:
        author_id = int(request.POST['id'])
        assert author_id != request.user.id
        tag_string = request.POST.get('tags', None)
        tags = pathToTags(tag_string)
        channel_author = models.User.objects.get(id = author_id)
        channels = models.UserChannel.objects.filter(author = channel_author)
        channel = None
        for item in channels:
            if utils.unorderedEqual(tags, item.tags.all()):
                channel = item
                break
        if channel is None:
            channel = models.UserChannel.objects.create(author = channel_author)
            for tag in tags:
                channel.tags.add(tag)
            channel.save()

        channel.createSubscription(request.user)
        return jsonResponse('Success') # Well, can't think of anything better
    except models.User.DoesNotExist:
        raise Http404, 'User does not exist'
    except models.Tag.DoesNotExist:
        raise Http404, 'Tag does not exist'


REDIRECT_FIELD_NAME = 'next'

def logout(request, redirect_field_name = REDIRECT_FIELD_NAME):
    auth.logout(request)
    redirect_to = request.REQUEST.get(redirect_field_name, '/')
    return HttpResponseRedirect(redirect_to)

@login_required
def searchUsersView(request):
    return render_to_response('stream/searchUsers.html', { 'viewer' : request.user })

@login_required
def searchUsersData(request):
    from stream.recommendations import searchUsers
    if request.method == "GET":
        if 'tags' in request.GET:
            tags = pathToNames(request.GET['tags'])
            if len(tags) > 0:
                result = searchUsers(request.user.id, tags)
            else:
                result = searchUsers(request.user.id)
        else:
            result = searchUsers(request.user.id)
        result = map(lambda x: { 'user' : x[0], 'tags' : x[1] }, result)
        context = {
            'User' : ['id', ('display_name', lambda x: x.get_profile().display_name)]
        }
        result = Converter(context).convert(result)
        return jsonResponse(result)

@login_required
def displaySettings(request):
    if request.method == 'GET':
        settings = models.DisplaySettings.objects.get_or_create(user = request.user)[0]
        context = { 'DisplaySettings' : ['display_tags', 'display_subscriptions'] }
        return jsonResponse(Converter(context).convert(settings))
    elif request.method == 'POST':
        settings = models.DisplaySettings.objects.get_or_create(user = request.user)[0]
        settings.display_tags = request.POST['display_tags']
        settings.display_subscriptions = request.POST['display_subscriptions']
        settings.save()
        return HttpResponse()
